﻿using Newtonsoft.Json;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using Zocono.Infrastructure.Common;
using Zocono.WCS.Domain.DomainRepository;
using Zocono.WCS.Domain.EntityInfo;
using Zocono.WCS.Domain.LogDomain;
using Zocono.WCS.Domain.PlcBytesObject;
using Zocono.WCS.Domain.PlcConnectPoolDomain;
using Zocono.WCS.Domain.ProcessModuleDomain;
using Zocono.WCS.Infrastructure.ApllicationDto.Enums;
using Zocono.WCS.Infrastructure.PlcCommon;
using Zocono.WCS.WMSApiDomain;
using Zocono.WCS.WMSApiDomain.WebApi;

namespace Zocono.WCS.Domain.EquipmentProcessDomain.PlanarGeneralMessage
{
    /// <summary>
    /// 到达下料台（DO）服务
    /// </summary>
    public class ArriveDoService : IPlanarGeneralPlcActionProcessService
    {
        private readonly ILogUtil _LogUtil;
        private readonly IPlcConnectionPool _PlcConnectionPool;
        private readonly IWorkTaskInfoRepository _WorkTaskInfoRepository;
        private readonly IPlcWriteDbInfoRepository _PlcWriteDbInfoRepository;
        private readonly IPlcConnectionInfoRepository _PlcConnectionInfoRepository;
        private readonly IPlcPointInfoRepositpry _IPlcPointInfoRepositpry;
        private readonly IPlcConnectionStateInfoRepository _IPlcConnectionStateInfoRepository;
        private readonly IRunLogInfoService _IRunLogInfoService;

        public ArriveDoService(ILogUtil logUtil
            , IPlcConnectionPool plcConnectionPool
            , IWorkTaskInfoRepository workTaskInfoRepository
            , IPlcWriteDbInfoRepository plcWriteDbInfoRepository
            , IPlcConnectionInfoRepository plcConnectionInfoRepository,
            IPlcPointInfoRepositpry plcPointInfoRepositpry
             , IPlcConnectionStateInfoRepository plcConnectionStateInfoRepository
            , IRunLogInfoService runLogInfoService)
        {
            _LogUtil = logUtil;
            _PlcConnectionPool = plcConnectionPool;
            _WorkTaskInfoRepository = workTaskInfoRepository;
            _PlcWriteDbInfoRepository = plcWriteDbInfoRepository;
            _PlcConnectionInfoRepository = plcConnectionInfoRepository;
            _IPlcPointInfoRepositpry = plcPointInfoRepositpry;
            _IPlcConnectionStateInfoRepository = plcConnectionStateInfoRepository;
            _IRunLogInfoService = runLogInfoService;
        }

        public async Task ProcessActionMessageAsync(BytesObjectBase plcActionMessage, BytesObjectBase wcsActionMessage, EquipmentDbObject planarEquipmentDbObject/*, BytesObjectBase plcStateMassge*/)
        {
            /*
             * 1. 一注高温浸润空托补给下架托盘到达堆垛机卸载位请求数据
             *     - 2, 0, 堆垛机卸载完成PLC上报托盘到达卸载位请求数据（PLC->WCS),WCS计算目标位置下发。
             * 2. 一注高温浸润完成满托下架托盘到达堆垛机卸载位请求数据
             *     - 1, 0, 堆垛机卸载完成PLC上报托盘到达卸载位请求数据（PLC->WCS),WCS计算目标位置下发。
             * 3. 化成托盘下架到达堆垛机卸载位申请数据
             *     - 1, 0, PLC上报托盘到达装载位（PLC->WCS),WCS执行调度堆垛机业务。
             * 4. 化成NG托盘下架到达堆垛机卸载位申请数据
             *     - 2, 0, PLC上报托盘到达装载位（PLC->WCS),WCS执行调度堆垛机业务。
             */

            /*
             * 处理逻辑
             * 1. 验证plc是否在线
             * 2. 验证当前设备是否报警
             * 3. 验证wcs任务，验证wcs当前子任务(待执行、执行中)，当前子任务已经是SC完成后更新了
             * 4. 组织plc报文，写入plc
             */
            var plcMessage = plcActionMessage as PlanarGeneralPlcActionBO;//Plc的动作报文
            var wcsMessage = wcsActionMessage as PlanarGeneralWcsActionBO;//Wcs的动作报文
            plcMessage.PalletBarcode = plcMessage.PalletBarcode.Trim();
            //var plcStateBo = plcStateMassge as PlcOnLineStateBO;//Plc在线离线报文 没有设备信息

            #region 查看堆垛机出库交互是否完成
            //int temp_value = 0;
            //var sc_list = SC_Manager.sc_list;
            //var ckt_code= plcMessage.EquipmentCode.ToUpper().Trim();
            //var sc_code = string.Empty;
            //if (ckt_code == "310135" || ckt_code == "340137")
            //{
            //    sc_code= await _IRunLogInfoService.GetScCodeByCKTCodeAsync(ckt_code);
            //}
            //if (!string.IsNullOrWhiteSpace(sc_code))
            //{
            //    foreach (var item in sc_list)
            //    {
            //        if (item.SCCode == sc_code)
            //        {
            //            temp_value++;
            //        }
            //        if (item.SCDownState == 1)
            //        {
            //            temp_value++;
            //        }
            //    }

            //    if (temp_value != 2)
            //    {
            //        RunLogInfo log = new RunLogInfo();
            //        log.ContainerBarcode = plcMessage.PalletBarcode;
            //        log.EquipmentCode = plcMessage.EquipmentCode;
            //        log.RunType = "出库台交互";

            //        log.RunLog = "堆垛机交互未完成";
            //        await _IRunLogInfoService.AddRunLogInfoAsync(log);
            //        return;
            //    }
            //}

            ////if (string.IsNullOrWhiteSpace(sc_code))
            ////{
            ////    RunLogInfo log = new RunLogInfo();
            ////    log.ContainerBarcode = plcMessage.PalletBarcode;
            ////    log.EquipmentCode = plcMessage.EquipmentCode;
            ////    log.RunType = "出库台交互";

            ////    log.RunLog = "出库台设备编码与堆垛机编码对应关系未设置";
            ////    await _IRunLogInfoService.AddRunLogInfoAsync(log);
            ////    return;
            ////}
            ////foreach (var item in sc_list)
            ////{
            ////    if (item.SCCode == sc_code)
            ////    {
            ////        temp_value++;
            ////    }
            ////    if (item.SCDownState == 1)
            ////    {
            ////        temp_value++;
            ////    }
            ////}

            ////if (temp_value!=2)
            ////{
            ////    RunLogInfo log = new RunLogInfo();
            ////    log.ContainerBarcode = plcMessage.PalletBarcode;
            ////    log.EquipmentCode = plcMessage.EquipmentCode;
            ////    log.RunType = "出库台交互";
                
            ////    log.RunLog = "堆垛机交互未完成";
            ////    await _IRunLogInfoService.AddRunLogInfoAsync(log);
            ////    return;
            ////}
            #endregion

            RunLogInfo runLogInfo = new RunLogInfo();
            runLogInfo.ContainerBarcode = plcMessage.PalletBarcode;
            runLogInfo.EquipmentCode = plcMessage.EquipmentCode;
            runLogInfo.RunType = "到达出库台";

            runLogInfo.RunLog = "到达出库台任务开始";

            var plcConn = _PlcConnectionPool.S7GetWritePlcConnection(plcMessage.PlcConnectionID);
            if (plcConn == null)
            {
                runLogInfo.RunLog = $"获取连接({plcMessage.PlcConnectionID})失败";
                await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);
                return;
            }

            var plcIPStates = await _IPlcConnectionStateInfoRepository.GetFirstAsync(l => l.PlcConnectionID == plcMessage.PlcConnectionID && l.Connected == true);
            if (plcIPStates == null)
            {
                runLogInfo.RunLog = $"{plcConn.IP}plc已离线";
                await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);
                return;
            }
            if (plcMessage.AlrmCode != 0)
            {
                runLogInfo.RunLog = $"报警,报警代码：{plcMessage.AlrmCode}";
                await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);
                return;
            }



            //3. 验证wcs任务，验证wcs当前子任务
            var exp = Expressionable.Create<WorkTaskInfo>();
            exp.AndIF(true, it => it.CurrentFromEquipment == plcMessage.EquipmentCode && it.TaskState == EnumTaskState.Executing && it.CurrentDetailTaskState == EnumDetailTaskState.Init &&
            it.TaskProcessState == EnumTaskProcessState.ScUnloaded);
            var task = await _WorkTaskInfoRepository.GetFirstAsync(exp.ToExpression());




            //未执行（待执行）
            //执行中
            //4. 组织报文，写入plc
            var objectType = typeof(PlanarGeneralWriteWcsAction).Name;
            var writeConfig = await _PlcWriteDbInfoRepository.GetFirstAsync(it => it.EquipmentCode == plcMessage.EquipmentCode && it.ObjectType == objectType);
            if (writeConfig == null)
            {
                runLogInfo.RunLog = $"获取PLC写入配置失败";
                await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);
                return;
            }
            PlanarGeneralWriteWcsAction writeAlrmBo = new PlanarGeneralWriteWcsAction(
                writeConfig.PlcConnectionId,
                writeConfig.ID,
                writeConfig.DbCode,
                writeConfig.DbStart,
                objectType,
                plcMessage.EquipmentCode.ToInt32(),
                0,
                //toEquiment.ToInt32(),
                0,
                0,
                "",
                0,
                0);


            if (task == null)
            {
                //出库台PLC重复申请再次下发
                //var distributeagain = Expressionable.Create<WorkTaskInfo>();
                //distributeagain.AndIF(true, wi => wi.CurrentFromEquipment == plcMessage.EquipmentCode && wi.TaskType == EnumTaskType.ATS && wi.TaskProcessState == EnumTaskProcessState.ScUnloaded && wi.TaskState == EnumTaskState.Executing && wi.CurrentDetailTaskState == EnumDetailTaskState.Executing);
                //var jieguo = await _WorkTaskInfoRepository.AsSugarClient().Queryable<WorkTaskInfo>().Where(distributeagain.ToExpression()).OrderBy(wi => wi.CurrentCreateTime, OrderByType.Desc).FirstAsync();
                //if (jieguo != null)
                //{
                //    writeAlrmBo.ActionType = plcMessage.ActionType;
                //    writeAlrmBo.ToEquipmentCode = jieguo.CurrentFromEquipment.ToInt32();
                //    writeAlrmBo.TaskCode = jieguo.TaskCode;
                //    writeAlrmBo.PalletBarcode = jieguo.PalletCode;
                //    writeAlrmBo.PalletType = plcMessage.PalletType;
                //    writeAlrmBo.AlrmCode = plcMessage.AlrmCode;


                //    var YesOrNo = await plcConn.WriteDbAsync(writeAlrmBo.DbCode, writeAlrmBo.DbStart, writeAlrmBo.Deserialize());
                //    if (YesOrNo == false)
                //    {
                //        runLogInfo.RunLog = $"设备号{jieguo.CurrentFromEquipment}PLC重复申请，WCS再次写入PLC失败";
                //        await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);
                //    }
                //    else
                //    {
                //        runLogInfo.RunLog = $"设备号{jieguo.CurrentFromEquipment}PLC重复申请，WCS再次写入PLC成功";
                //        await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);
                //    }
                //    return;
                //}

                runLogInfo.RunLog = $"请查看堆垛机是否报完成，如若未报完成，请将此托盘放回原来库位，重置任务等待堆垛机再次取料";
                await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);
                writeAlrmBo.AlrmCode = 5;
                var res = await plcConn.WriteDbAsync(writeAlrmBo.DbCode, writeAlrmBo.DbStart, writeAlrmBo.Deserialize());
                return;
            }
            //bool isSuccess = false;
            //int retryCount = 0;
            //while (!isSuccess && retryCount < 3) // 最多重试3次
            //{
            //    try
            //    {
            //        // 检查是否有正在执行的任务
            //        if (task == null)
            //        {
            //            var res = await plcConn.WriteDbAsync(writeAlrmBo.DbCode, writeAlrmBo.DbStart, writeAlrmBo.Deserialize());
            //            runLogInfo.RunLog = $"请查看堆垛机是否报完成，如若未报完成，请将此托盘放回原来库位，重置任务等待堆垛机再次取料，系统尝试重写";
            //            await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);
            //            writeAlrmBo.AlrmCode = 5;
            //            retryCount++;
            //            await Task.Delay(1000); // 等待1秒后重试
            //        }
            //        else
            //        {
            //            // 执行正常的写入操作
            //            await plcConn.WriteDbAsync(writeAlrmBo.DbCode, writeAlrmBo.DbStart, writeAlrmBo.Deserialize());
            //            isSuccess = true; // 写入成功，退出循环
            //            runLogInfo.RunLog = $"重写成功";
            //        }
            //    }
            //    catch (Exception ex)
            //    {
            //        // 处理写入异常
            //        Console.WriteLine($"写入数据时发生异常：{ex.Message}");
            //        runLogInfo.RunLog = $"重复写入异常";
            //        retryCount++;
            //        await Task.Delay(1000); // 等待1秒后重试
            //    }
            //}


            //var expr=Expressionable.Create<WorkTaskInfo,RouteDetailInfo>();
            //expr.AndIF(true, (l, m) => l.CurrentRounteDetailID == m.PrevionsID && m.IsEnd == true);
            //var rounteDetail = await _WorkTaskInfoRepository.AsSugarClient().Queryable<WorkTaskInfo, RouteDetailInfo>((l, m) => new JoinQueryInfos(JoinType.Inner, l.CurrentRounteDetailID == m.PrevionsID))
            //    .Where(expr.ToExpression())
            //    .Select((l, m) => m).FirstAsync();

            if (task.CurrentDetailTaskState == EnumDetailTaskState.Finish)
            {
                runLogInfo.RunLog = $"处理成功当前子任务状态完成无需操作";
                await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);
                return;
            }


            ////在DO台申请目标位置
            //long toEquiment = await WCSMessageWithWMSApi.WCSGetDoToEquimentAsync(task.PalletCode);

            //获取配置巷道信息
            var routeConfigExp = Expressionable.Create<RouteInfo>();
            routeConfigExp.AndIF(true, (a) => a.FromStation == task.WmsFromStation && a.ToStation == task.WmsToStation);
            var routeInfo = await _WorkTaskInfoRepository.AsSugarClient()
                .Queryable<RouteInfo>()
                .Where(routeConfigExp.ToExpression())
                .Select((a) => new RouteInfo()
                {
                    ID = a.ID,
                    FromStation = a.FromStation,
                    ToStation = a.ToStation,
                }).FirstAsync();
            if (routeInfo == null)
            {
                runLogInfo.RunLog = $"起始工位{task.WmsFromStation}目标工位{task.WmsToStation}获取失败";
                await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);

                writeAlrmBo.AlrmCode = 6;
                var res = await plcConn.WriteDbAsync(writeAlrmBo.DbCode, writeAlrmBo.DbStart, writeAlrmBo.Deserialize());
                return;
            }

            //巷道详情信息获取
            var routeDetailConfigExp = Expressionable.Create<RouteDetailInfo>();
            routeDetailConfigExp.AndIF(true, (a) => a.RouteID == routeInfo.ID && a.IsEnd);
            var routeDetailInfo = await _WorkTaskInfoRepository.AsSugarClient()
                .Queryable<RouteDetailInfo>()
                .Where(routeDetailConfigExp.ToExpression())
                .Select((a) => new
                {
                    ID = a.ID,
                    RouteID = a.RouteID,
                        //FromCargo = a.FromCargo,
                        //ToCargo = a.ToCargo,
                        //DispatchType = a.DispatchType,
                        //FromEquipment = a.FromEquipment,
                    ToEquipment = a.ToEquipment,

                }).FirstAsync();



            //plc报文什么握手类型则回什么握手类型
            PlanarGeneralWriteWcsAction writeBo = new PlanarGeneralWriteWcsAction(
                writeConfig.PlcConnectionId,
                writeConfig.ID,
                writeConfig.DbCode,
                writeConfig.DbStart,
                objectType,
                plcMessage.EquipmentCode.ToInt32(),
                plcMessage.ActionType,
                //toEquiment.ToInt32(),
                routeDetailInfo.ToEquipment.ToInt32(),
                task.TaskCode,
                task.PalletCode,
                plcMessage.PalletType,
                alrmCode: plcMessage.AlrmCode);

            WMSApiDomain.WebApi.HeaderResult<bool> result = await WCSMessageWithWMSApi.WCSNoticePalltetArriveDoEquipmentAsync(task.WmsTaskCode);
            if (result.IsSucceed)
            {
                runLogInfo.RunLog = $"WMS释放货位成功{task.PalletCode}";
            }
            else
            {
                runLogInfo.RunLog = $"WMS释放货位失败{task.PalletCode}：{result.Message}";
                await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);
                writeBo.ActionType = 0;
                writeBo.AlrmCode = 46;
                var writelose = await plcConn.WriteDbAsync(writeBo.DbCode, writeBo.DbStart, writeBo.Deserialize());
                if (writelose == false)
                {
                    runLogInfo.RunLog = $"PLC(IP{plcConn.IP})写入报文失败";
                    await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);
                    return;
                }
                else
                {
                    runLogInfo.RunLog = $"PLC(IP{plcConn.IP})写入报文成功";
                    await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);
                }
                return;
            }
            await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);

            //托盘类型
            switch (task.PalletCode.Substring(1, 1))
            {
                case "A":
                    writeBo.PalletType = 2;
                    break;
                case "B":
                    writeBo.PalletType = 1;
                    break;
                case "C":
                    writeBo.PalletType = 3;
                    break;
            }

            //前面都通过，则任务开始
            await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);
            runLogInfo.RunLog = $"PLC写入报文内容： {JsonConvert.SerializeObject(writeBo)}";
            await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);
            var writeResult = await plcConn.WriteDbAsync(writeBo.DbCode, writeBo.DbStart, writeBo.Deserialize());
            if (writeResult == false)
            {
                runLogInfo.RunLog = $"PLC(IP{plcConn.IP})写入报文失败";
                await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);
                return;
            }
            else
            {
                runLogInfo.RunLog = $"PLC(IP{plcConn.IP})写入报文成功";
                await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);
            }

            task.CurrentToEquipment = routeDetailInfo.ToEquipment.ToString();
            task.CurrentDetailTaskState = EnumDetailTaskState.Executing;
            task.UpdateTime = DateTime.Now;
            await _WorkTaskInfoRepository.UpdateAsync(task);



            //bool writeSucceeded = false;

            //while (!writeSucceeded)
            //{
            //    // 执行写入操作
            //    var writeResult = await plcConn.WriteDbAsync(writeBo.DbCode, writeBo.DbStart, writeBo.Deserialize());
            //    if (writeResult == false)
            //    {
            //        // 如果写入失败，记录日志并继续下一轮写入
            //        runLogInfo.RunLog = $"PLC(IP{plcConn.IP})写入报文失败";
            //        await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);
            //    }
            //    else
            //    {
            //        // 如果写入成功，记录日志并跳出循环
            //        runLogInfo.RunLog = $"PLC(IP{plcConn.IP})写入报文成功";
            //        await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);
            //        writeSucceeded = true;
            //    }
            //}

            //// 更新任务对象
            //task.CurrentToEquipment = routeDetailInfo.ToEquipment.ToString();
            //task.CurrentDetailTaskState = EnumDetailTaskState.Executing;
            //task.UpdateTime = DateTime.Now;
            //await _WorkTaskInfoRepository.UpdateAsync(task);

            // 记录运行日志
            runLogInfo.RunLog = $"PLC写入报文内容： {JsonConvert.SerializeObject(writeBo)}";
            await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);




            #region 出库台交互完成，重置堆垛机状态
            //if (!string.IsNullOrWhiteSpace(sc_code))
            //{
            //    foreach (var item in sc_list)
            //    {
            //        if (item.SCCode == sc_code)
            //        {
            //            item.SCDownState = 0;
            //        }
            //    }
            //}
            #endregion


            runLogInfo.RunLog = $"到达DO处理成功";
            await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);




        }
    }
}
